import random, sys, traceback

from multiprocessing import Process, JoinableQueue, Event
from datetime import datetime

import lib, sql

from xdh_logging import initLog, LogWriter
from oper import Cleaner, Processor
from holiday_subfile import HolidaySubFile
from holiday_catfile import HolidayCatFile
from settings import settings
from cache import cache
from nothing import Nothing
from tpd import TPD

log = initLog("holidaylist")

class HolidayWorker(Process):
	Q = JoinableQueue()
	started = Event()

	@classmethod
	def startup(cls, favs = None, proc = None, clean = None):
		if not cls.is_running():
			cls.started.set()
			cls.workers = []
			for i in xrange(settings.data.num_processes):
				worker = Nothing()
				setattr(worker, "running", Event())
				setattr(worker, "process", cls(worker.running, favs, proc, clean))
				cls.workers.append(worker)

	@classmethod
	def is_running(cls):
		if not hasattr(cls, "workers"):
			return False

		running = not (cls.Q.empty() and cls.started.is_set())

		for worker in cls.workers:
			if worker.process.is_alive():
				running |= worker.running.is_set()

		return running

	@classmethod
	def shutdown(cls):
		if not hasattr(cls, "workers"):
			return

		for worker in cls.workers:
			if worker.process.is_alive():
				log.debug("Sending stop to {name} Queue.".format(name = cls.__name__))
				cls.Q.put(lib.STOP)

		cls.Q.join()

		for worker in cls.workers:
			if worker.process.is_alive():
				worker.process.join()

		del cls.workers

		sql.Q.join()

		cls.started.clear()


	def __init__(self, running, favs = None, proc = None, clean = None):
		super(HolidayWorker, self).__init__()

		self.running = running

		if favs == None:
			self.favs = [False, True]
		else:
			self.favs = favs

		if clean == None:
			self.cleaners = Cleaner.files()
		else:
			Cleaner.set_files(clean)
			self.cleaners = clean

		if proc == None:
			self.processors = Processor.files()
		else:
			Processor.set_files(proc)
			self.processors = proc

		self.start()

	def subProcess(self):
		start = datetime.today()
		log.info("<{holiday}> Beginning Processing".format(holiday = self.holiday))
		
		random.shuffle(self.favs)

		cleaner_start = datetime.today()
		Cleaner.batch(self, self.favs)
		cleaner_diff = datetime.today() - cleaner_start
		self.write_stat("Cleaners Time", cleaner_diff.total_seconds())

		proc_favs = []

		if False in self.favs:
			proc_favs.append(False)

		if True in self.favs:
			proc_favs.append(True)

		processor_start = datetime.today()
		Processor.batch(self, proc_favs)
		processor_diff = datetime.today() - processor_start
		self.write_stat("Processor Time", processor_diff.total_seconds())

		log.info("<{holiday}> Processing Complete".format(holiday = self.holiday))
		diff = datetime.today() - start
		self.write_stat("Total Processing Time", diff.total_seconds())

		totals = {}

		for key, tracks in self.cat_tracks.data.iteritems():
			sub, fav = key[0], key[1]

			if fav:
				sub = "f" + sub

			totals[sub] = len(tracks)

	def initialize(self, holiday):
		self.holiday = holiday
		self.stats = []

		self.weight = lib.holidayWeight(self.holiday)

		self.write_stat("Weight", self.weight)

		start = datetime.today()
		self.tracks = HolidayCatFile(self.holiday)
		diff = datetime.today() - start
		self.write_stat("Holiday Load Time", diff.total_seconds())
		self.write_stat("Total Tracks", len(self.tracks))


	def run(self):
		try:
			while True:
				item = HolidayWorker.Q.get()
				if item == lib.STOP:
					HolidayWorker.Q.task_done()
					log.debug("Got stop from {name} Queue.".format(name = HolidayWorker.__name__))
					break

				self.running.set()

				if not HolidayWorker.started.is_set():
					HolidayWorker.started.set()

				self.initialize(item)

				log.info("<{holiday}> Starting processing".format(holiday = self.holiday))
                                
				with HolidaySubFile(self.holiday) as self.cat_tracks:
					log.info("<{holiday}> Loaded Sublist File".format(holiday = self.holiday))
                                
					self.subProcess()
                                
				self.finish_stats()

				HolidayWorker.Q.task_done()

				self.running.clear()
                                
				log.info("<{holiday}> Finished".format(holiday = self.holiday))
		except Exception, e:
			log.error("{name}: {msg}".format(name = e.__class__.__name__, msg = e))
			log.error("".join(traceback.format_exception(*sys.exc_info())))

	def finish_stats(self):
		sql.process(sql.HolidayCatStatData, "insert", self.stats)

	def write_stat(self, step, value):
		if value > 0:
			data = { 
				'timestamp': datetime.today(),
				'holiday': self.holiday,
				'step': step,
				'value': value,
			}

			self.stats.append(data)


class HolidayList(object):
	def __init__(self, holidays = None, favs = None, clean = None, proc = None):
		if holidays == None:
			self.holidays = HolidayCatFile.getHolidayList()
		else:
			self.holidays = holidays

		self.favs = favs
		self.clean = clean
		self.proc = proc

	def __enter__(self):
		return self

	def __exit__(self, type, value, tback):
		HolidayWorker.shutdown()

		sql.Q.join()

		if type != None:
			log.error("{name}: {msg}".format(name = value.__class__.__name__, msg = value))
			log.error("".join(traceback.format_exception(*sys.exc_info())))

	def start(self):
		dels = []
		for holiday in lib.holidays(current = False):
			dels.append({"holiday": holiday})

		if len(dels) > 0:
			sql.process(sql.HolidayData, "delete", dels)
		
		if len(self.holidays) > 0:
			HolidayWorker.started.set()
			HolidayWorker.startup(self.favs, self.clean, self.proc)


			random.shuffle(self.holidays)

			for holiday in self.holidays:
				HolidayWorker.Q.put(holiday)
		else:
			HolidayWorker.started.clear()

	

	def stop(self):
		if len(self.holidays) > 0:
			HolidayWorker.shutdown()

	@property
	def running(self):
		return HolidayWorker.is_running()

	@classmethod
	def process(cls, holidays = None, favs = None, clean = None, proc = None):
		with HolidayList(holidays, favs, clean, proc) as sublist:
			sublist.start()

			while sublist.running:
				time.sleep(settings.data.sleep)

			sublist.stop()
